
<!doctype html>
<html>
	<head>
		<title>test page template</title>
		<style>
			#demo {
				overflow:scroll;
				margin-top:5em;
			}
		
			.w { 
				width:5em;
				padding:1em;
				position:absolute;
				border: 1px solid black;
				z-index:5;				
			}
			._jsPlumb_connector {
				z-index:4;
			}
			
			._jsPlumb_endpoint {
				z-index:6;
			}

			.dragHover { border:1px dotted red; }

			#w1 {
				width:10em;
				height:10em;
				top:13em;
				left:13em;
			}

			#w2 {
				top:17em;
				left:12em;
				height:5em;
				width:5em;
			}

			#w3 {
				top:21em;
				left:21em;
				width:7em;
				height:5em;
			}

			#w4 {
				top:6em;
				left:18em;
				height:7em;
				width:12em;
			}

			#w5 {
				top:10em;
				left:10em;
				width:5em;
				height:5em;
			}
			
		</style>
	</head>
	<body>
		<div id="demo">
			<button id="go" style="display:none;position:absolute;top:0;left:0;z-index:5000;">go</button>
			<div id="w1" class="w">one</div>
			<div id="w2" class="w">two</div>
			<div id="w3" class="w">three</div>				
			<div id="w4" class="w">four</div>	
			<div id="w5" class="w">five</div>	
		</div>
	
	 	<script type="text/javascript" src="../../lib/jquery-1.9.0-min.js"></script>
		<script type="text/javascript" src="../../lib/jquery-ui-1.9.2-min.js"></script>
		<script type="text/javascript" src="../../lib/jsBezier-0.6-min.js"></script>
		<script type="text/javascript" src="../../build/js/jquery.jsPlumb-1.4.0-all.js"></script>					            			
		
		<script>				
			jsPlumb.ready(function() {
				$("#go").show();
				$("#go").click(function() {
					var order = ["w5", "w4", "w1", "w2", "w3"],  // i know this but in the layouts it will have been computed.		
						jpcl = jsPlumb.CurrentLibrary,
						padding = 40;

					/* 
					this is an array of functions that tell you how much to move the second element by in the x and y directions.  the index to this array is the segment that the second element is in (index 0 is null because segments are reported as 1..4.).  
					
					the general principle here is that we are going to trace the line between the center of the two elements, and extend the second element along this line until it is sufficiently far from the first element.  once both elements are in place, this line will cross one edge of each of these elements; that edge is dependent on the segment the second element is in, and for each segment can be one of two edges. for instance, for segment three, wich equates to 6 - 9 on a clock, the center line could cross the first element on the bottom edge or the left edge, and it could be the top or right edge of the second element.  note that this is not necessarily the case when we start; the center of the second element may be contained within the first element.

					so to calculate how far to extend the second element along the center line, we need to know which face the center line will cross that element on.  this is a simple y=mx+b problem, unless m=infinity, in which case we know it's either the top or the bottom (top in the case of segments 1 and 4, bottom in the case of segments 2 and 3).  we actually do not need to know the segment it crosses the first element at, since all we need to ensure is that the second element is far enough away, and the edge on which the center lines meets that element tells us which plane (x or y) to move it in.  remember the point of this is not to fling elements away in every direction; it's just to ensure that they have sufficient padding.  consider a very tall but narrow element 2, say 50x1000 pixels, and a small element 1, say 150x150, with the second element cutting straight through the first one, left of center.  when we move this, all we want to do is move it over to the left so that there is padding between it and element 1.  it's pretty clear that the center line will cross element 2's right edge, and it's that simple: since we know the edge it crosses the second element on, we just adjust the second element's position until its right edge is padded sufficiently from element 1's left edge.

					of course the center line is on some slope, and so the actual delta in the given plane will have to be the result of a little but of pythagoras.

					for x, to solve for d:

					d.cos(theta) = distance
					d = distance / cos(theta)

					for y:

					d = distance / sin(theta)
				*/

					var isOnHorizontalEdge = function(r, x) {
						return (r.x <= x && x <= r.x + r.w);
					},
					isOnEdge = function(r, axis, dim, v) {
						return (r[axis] <= v && v <= r[axis] + r[dim]);	
					},
					adjusters = [
						null,
						function(r1, r2, m, b, x, y) {
							// segment one
							// y is the same as segment 4
							// x is the same as segment 2
							//var y = r2.y + r2.h, x =(m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) :  (y - b) / m;
							if (isOnEdge(r2, "x", "w", x)) {							
								return {
									left:0,
									top:r1.y - (r2.y + r2.h)
								}
							}
							else {
								
								// x is fixed, try y.
					//			x = r2.x; 
					//			y = (m * x) + b;
					//			if (r2.y < y && y < r2.y + r2.h) {
									console.log("right edge");
									return {
										left:(r1.x +r1.w) -  r2.x,
										top:0
									}
								}
					//		}
						},
						function(r1, r2, m, b, x, y) { // segment two
							//var y = r2.y, x =(m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) :  (y - b) / m;
							if (isOnEdge(r2, "x", "w", x)) {								
								return {
									left:0,
									top:r1.y + r1.h - r2.y
								}
							}
							else {								
					//			x = r2.x; 
					//			y = (m * x) + b;
					//			if (r2.y < y && y < r2.y + r2.h) {								
									return {
										left:(r1.x +r1.w) -  r2.x,
										top:0
									}
					//			}
							}
							
						},
						function(r1, r2, m, b, x, y) {  // segment three 							
							if (isOnEdge(r2, "x", "w", x)) {							
								return {
									left:0,
									top:r1.y + r1.h - r2.y
								}
							}
							else {								
					//			x = r2.x + r2.w; 
					//			y = (m * x) + b;
					//			if (r2.y < y && y < r2.y + r2.h) {									
									return {
										left:r1.x - (r2.x + r2.w),
										top:0
									}
					//			}
							}


						},
						function(r1, r2, m, b, x, y) { // segment four							
							if (isOnEdge(r2, "x", "w", x)) {								
								return {
									left:0,
									top:r1.y - (r2.y + r2.h)
								}
							}
							else {								
					//			x = r2.x + r2.w; 
					//			y = (m * x) + b;
					//			if (r2.y < y && y < r2.y + r2.h) {													
									return {
										left:r1.x - (r2.x + r2.w),
										top:0
									}
					//			}
							}
						}						
					];

					for (var i = 0; i < order.length; i++) {
						var e1 = $("#" + order[i]), o1 = jpcl.getOffset(e1), s1 = jpcl.getSize(e1),
							r1 = { x:o1.left - padding, y: o1.top - padding, w:s1[0] + (2 * padding), h:s1[1] + (2 * padding) };
						for (var j = 0; j < order.length; j++) {
							var e2 = $("#" + order[j]);
							if (i != j) {
								var e2 = $("#" + order[j]), o2 = jpcl.getOffset(e2), s2 = jpcl.getSize(e2),
									r2 = { x:o2.left - padding, y: o2.top - padding, w:s2[0] + (2 * padding), h:s2[1] + (2 * padding) };

								if (jsPlumbUtil.intersects(r1, r2)) {									
									var c1 = [ r1.x + (r1.w / 2), r1.y + (r1.h / 2) ],
										c2 = [ r2.x + (r2.w / 2), r2.y + (r2.h / 2) ],
										m = jsPlumbUtil.gradient(c1, c2),
										s = jsPlumbUtil.segment(c1, c2),
										b = (m == Infinity || m == -Infinity) ? 0 : c1[1] - (m * c1[0]);

									console.log(order[i] + " intersects with " + order[j] + "; " + order[j] + " is in segment " + s);

									var y = r2.y + r2.h, x =(m == Infinity || m == -Infinity) ? r2.x + (r2.w / 2) :  (y - b) / m,
										adjustBy = adjusters[s](r1, r2, m, b, x, y);
									
									jpcl.setOffset(e2, {
										left:o2.left + adjustBy.left,
										top:o2.top + adjustBy.top
									});

								}
							}
						}
					}
				});				
			});
		</script>
	</body>
</html>
